/*
 * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;

import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.ArrayList;
import java.security.AccessController;
import java.security.SecureRandom;
import java.nio.file.Path;
import java.nio.file.FileSystems;
import sun.security.action.GetPropertyAction;

/**
 * 文件和目录路径名的抽象表示。
 *
 * <p> 用户界面和操作系统使用系统相关的<em>路径名字符串</em>来命名文件和目录。
 * 此类提供了层次化路径名的抽象、系统无关的视图。<em>抽象路径名</em>有两个组件：
 *
 * <ol>
 * <li> 一个可选的系统相关<em>前缀</em>字符串，
 *      比如磁盘驱动器说明符、UNIX根目录的<code>"/"</code>，
 *      或者Microsoft Windows UNC路径名的<code>"\\\\"</code>，以及
 * <li> 零个或多个字符串<em>名称</em>的序列。
 * </ol>
 *
 * 抽象路径名中的第一个名称可以是目录名，或者在Microsoft Windows UNC路径名的情况下是主机名。
 * 抽象路径名中的每个后续名称表示一个目录；最后一个名称可以表示目录或文件。
 * <em>空</em>抽象路径名没有前缀，名称序列为空。
 *
 * <p> 路径名字符串与抽象路径名之间的转换本质上是系统相关的。
 * 当抽象路径名转换为路径名字符串时，每个名称与下一个名称之间用单个默认<em>分隔符字符</em>分隔。
 * 默认名称分隔符字符由系统属性<code>file.separator</code>定义，
 * 并在该类的公共静态字段<code>{@link #separator}</code>和<code>{@link #separatorChar}</code>中提供。
 * 当路径名字符串转换为抽象路径名时，其中的名称可以由默认名称分隔符字符分隔，
 * 也可以由底层系统支持的任何其他名称分隔符字符分隔。
 *
 * <p> 路径名（无论是抽象的还是字符串形式）可以是<em>绝对</em>或<em>相对</em>的。
 * 绝对路径名是完整的，不需要其他信息来定位它所表示的文件。
 * 相反，相对路径名必须根据从其他路径名获取的信息来解释。
 * 默认情况下，<code>java.io</code>包中的类始终根据当前用户目录解析相对路径名。
 * 该目录由系统属性<code>user.dir</code>命名，通常是调用Java虚拟机的目录。
 *
 * <p> 抽象路径名的<em>父级</em>可以通过调用此类的{@link #getParent}方法获得，
 * 它由路径名的前缀和路径名名称序列中除最后一个之外的所有名称组成。
 * 每个目录的绝对路径名是以该目录绝对路径名开头的任何具有绝对抽象路径名的<tt>File</tt>对象的祖先。
 * 例如，由抽象路径名<tt>"/usr"</tt>表示的目录是由路径名<tt>"/usr/local/bin"</tt>表示的目录的祖先。
 *
 * <p> 前缀概念用于处理UNIX平台上的根目录，以及Microsoft Windows平台上的驱动器说明符、根目录和UNC路径名，如下所示：
 *
 * <ul>
 *
 * <li> 对于UNIX平台，绝对路径名的前缀始终是<code>"/"</code>。
 * 相对路径名没有前缀。表示根目录的抽象路径名的前缀是<code>"/"</code>，名称序列为空。
 *
 * <li> 对于Microsoft Windows平台，包含驱动器说明符的路径名的前缀由驱动器字母后跟<code>":"</code>组成，
 * 如果路径名是绝对的，则可能后跟<code>"\\"</code>。
 * UNC路径名的前缀是<code>"\\\\"</code>；主机名和共享名是名称序列中的前两个名称。
 * 不指定驱动器的相对路径名没有前缀。
 *
 * </ul>
 *
 * <p> 此类的实例可能表示也可能不表示实际的文件系统对象，如文件或目录。
 * 如果它确实表示这样的对象，则该对象驻留在<em>分区</em>中。
 * 分区是文件系统的操作系统特定存储部分。单个存储设备（例如物理磁盘驱动器、闪存、CD-ROM）可能包含多个分区。
 * 对象（如果有）将驻留在由该路径名绝对形式的某个祖先<em>命名</em>的分区上。
 *
 * <p> 文件系统可能对实际文件系统对象实施某些操作的限制，例如读取、写入和执行。
 * 这些限制统称为<em>访问权限</em>。文件系统可能对单个对象具有多组访问权限。
 * 例如，一组可能适用于对象的<em>所有者</em>，另一组可能适用于所有其他用户。
 * 对象上的访问权限可能导致此类中的某些方法失败。
 *
 * <p> <code>File</code>类的实例是不可变的；也就是说，一旦创建，<code>File</code>对象表示的抽象路径名将永远不会改变。
 *
 * <h3>与{@code java.nio.file}包的互操作性</h3>
 *
 * <p> <a href="../../java/nio/file/package-summary.html">{@code java.nio.file}</a>包
 * 定义了Java虚拟机访问文件、文件属性和文件系统的接口和类。
 * 此API可用于克服{@code java.io.File}类的许多限制。
 * {@link #toPath toPath}方法可用于获取{@link Path}，
 * 它使用{@code File}对象表示的抽象路径来定位文件。
 * 生成的{@code Path}可与{@link java.nio.file.Files}类一起使用，
 * 以提供对附加文件操作、文件属性和I/O异常的更有效和广泛的访问，
 * 以帮助诊断文件操作失败时的错误。
 *
 * @author  未署名
 * @since   JDK1.0
 */

public class File
    implements Serializable, Comparable<File>
{

    /**
     * 表示平台本地文件系统的FileSystem对象。
     */
    private static final FileSystem fs = DefaultFileSystem.getFileSystem();

    /**
     * 此抽象路径名的规范化路径名字符串。规范化的
     * 路径名字符串使用默认的名称分隔符字符，并且不
     * 包含任何重复或冗余的分隔符。
     *
     * @serial
     */
    private final String path;

    /**
     * 指示文件路径状态的枚举类型。
     */
    private static enum PathStatus { INVALID, CHECKED };

    /**
     * 指示文件路径是否无效的标志。
     */
    private transient PathStatus status = null;

    /**
     * 检查文件是否具有无效路径。目前，对文件路径的检查
     * 非常有限，它只涵盖空字符检查，除非通过系统属性
     * 明确启用进一步检查。返回true意味着路径绝对无效/垃圾，
     * 但返回false并不保证路径有效。
     *
     * @return 如果文件路径无效则返回true。
     */
    final boolean isInvalid() {
         PathStatus s = status;
        if (status == null) {
            s = fs.isInvalid(this) ? PathStatus.INVALID : PathStatus.CHECKED;
            status = s;
        }
        return status == PathStatus.INVALID;
    }

    /**
     * 此抽象路径名前缀的长度，如果没有前缀则为零。
     */
    private final transient int prefixLength;

    /**
     * 返回此抽象路径名前缀的长度。
     * 供FileSystem类使用。
     */
    int getPrefixLength() {
        return prefixLength;
    }

    /**
     * 系统相关的默认名称分隔符字符。此字段被初始化为包含
     * 系统属性<code>file.separator</code>值的第一个字符。
     * 在UNIX系统上，此字段的值是<code>'/'</code>；
     * 在Microsoft Windows系统上是<code>'\\'</code>。
     *
     * @see     java.lang.System#getProperty(java.lang.String)
     */
    public static final char separatorChar = fs.getSeparator();

    /**
     * 系统相关的默认名称分隔符字符，为方便起见表示为字符串。
     * 此字符串包含单个字符，即<code>{@link #separatorChar}</code>。
     */
    public static final String separator = "" + separatorChar;

    /**
     * 系统相关的路径分隔符字符。此字段被初始化为包含
     * 系统属性<code>path.separator</code>值的第一个字符。
     * 此字符用于分隔作为<em>路径列表</em>给出的文件序列中的文件名。
     * 在UNIX系统上，此字符是<code>':'</code>；
     * 在Microsoft Windows系统上是<code>';'</code>。
     *
     * @see     java.lang.System#getProperty(java.lang.String)
     */
    public static final char pathSeparatorChar = fs.getPathSeparator();

    /**
     * 系统相关的路径分隔符字符，为方便起见表示为字符串。
     * 此字符串包含单个字符，即<code>{@link #pathSeparatorChar}</code>。
     */
    public static final String pathSeparator = "" + pathSeparatorChar;


    /* -- 构造函数 -- */

    /**
     * 用于已规范化路径名字符串的内部构造函数。
     */
    private File(String pathname, int prefixLength) {
        this.path = pathname;
        this.prefixLength = prefixLength;
    }

    /**
     * 用于已规范化路径名字符串的内部构造函数。
     * 参数顺序用于区分此方法与public(File, String)构造函数。
     */
    private File(String child, File parent) {
        assert parent.path != null;
        assert (!parent.path.equals(""));
        this.path = fs.resolve(parent.path, child);
        this.prefixLength = parent.prefixLength;
    }

    /**
     * 通过将给定的路径名字符串转换为抽象路径名来创建新的<code>File</code>实例。
     * 如果给定的字符串是空字符串，则结果是空的抽象路径名。
     *
     * @param   pathname  路径名字符串
     * @throws  NullPointerException
     *          如果<code>pathname</code>参数为<code>null</code>
     */
    public File(String pathname) {
        if (pathname == null) {
            throw new NullPointerException();
        }
        this.path = fs.normalize(pathname);
        this.prefixLength = fs.prefixLength(this.path);
    }

    /* 注意：双参数File构造函数不会将空的父抽象路径名解释为当前用户目录。
       空的父路径名会导致子路径名根据FileSystem.getDefaultParent方法定义的
       系统相关目录进行解析。在Unix上，此默认值是"/"，
       而在Microsoft Windows上是"\\"。这是为了与此类的原始行为兼容所必需的。 */

    /**
     * 从父路径名字符串和子路径名字符串创建新的<code>File</code>实例。
     *
     * <p> 如果<code>parent</code>为<code>null</code>，则新的
     * <code>File</code>实例的创建就像在给定的<code>child</code>
     * 路径名字符串上调用单参数<code>File</code>构造函数一样。
     *
     * <p> 否则，<code>parent</code>路径名字符串被视为表示目录，
     * <code>child</code>路径名字符串被视为表示目录或文件。
     * 如果<code>child</code>路径名字符串是绝对的，则以系统相关的方式
     * 将其转换为相对路径名。如果<code>parent</code>是空字符串，
     * 则通过将<code>child</code>转换为抽象路径名并根据系统相关的
     * 默认目录解析结果来创建新的<code>File</code>实例。
     * 否则，每个路径名字符串都转换为抽象路径名，
     * 子抽象路径名根据父路径名进行解析。
     *
     * @param   parent  父路径名字符串
     * @param   child   子路径名字符串
     * @throws  NullPointerException
     *          如果<code>child</code>为<code>null</code>
     */
    public File(String parent, String child) {
        if (child == null) {
            throw new NullPointerException();
        }
        if (parent != null) {
            if (parent.equals("")) {
                this.path = fs.resolve(fs.getDefaultParent(),
                                       fs.normalize(child));
            } else {
                this.path = fs.resolve(fs.normalize(parent),
                                       fs.normalize(child));
            }
        } else {
            this.path = fs.normalize(child);
        }
        this.prefixLength = fs.prefixLength(this.path);
    }

    /**
     * 从父抽象路径名和子路径名字符串创建新的<code>File</code>实例。
     *
     * <p> 如果<code>parent</code>为<code>null</code>，则新的
     * <code>File</code>实例的创建就像在给定的<code>child</code>
     * 路径名字符串上调用单参数<code>File</code>构造函数一样。
     *
     * <p> 否则，<code>parent</code>抽象路径名被视为表示目录，
     * <code>child</code>路径名字符串被视为表示目录或文件。
     * 如果<code>child</code>路径名字符串是绝对的，则以系统相关的方式
     * 将其转换为相对路径名。如果<code>parent</code>是空的抽象路径名，
     * 则通过将<code>child</code>转换为抽象路径名并根据系统相关的
     * 默认目录解析结果来创建新的<code>File</code>实例。
     * 否则，每个路径名字符串都转换为抽象路径名，
     * 子抽象路径名根据父路径名进行解析。
     *
     * @param   parent  父抽象路径名
     * @param   child   子路径名字符串
     * @throws  NullPointerException
     *          如果<code>child</code>为<code>null</code>
     */
    public File(File parent, String child) {
        if (child == null) {
            throw new NullPointerException();
        }
        if (parent != null) {
            if (parent.path.equals("")) {
                this.path = fs.resolve(fs.getDefaultParent(),
                                       fs.normalize(child));
            } else {
                this.path = fs.resolve(parent.path,
                                       fs.normalize(child));
            }
        } else {
            this.path = fs.normalize(child);
        }
        this.prefixLength = fs.prefixLength(this.path);
    }

    /**
     * 通过将给定的<tt>file:</tt> URI转换为抽象路径名来创建新的<tt>File</tt>实例。
     *
     * <p> <tt>file:</tt> URI的确切形式是系统相关的，因此
     * 此构造函数执行的转换也是系统相关的。
     *
     * <p> 对于给定的抽象路径名<i>f</i>，保证
     *
     * <blockquote><tt>
     * new File(</tt><i>&nbsp;f</i><tt>.{@link #toURI() toURI}()).equals(</tt><i>&nbsp;f</i><tt>.{@link #getAbsoluteFile() getAbsoluteFile}())
     * </tt></blockquote>
     *
     * 只要原始抽象路径名、URI和新抽象路径名都在同一个Java虚拟机的
     * （可能不同的调用中）创建。但是，当在一个操作系统的虚拟机中
     * 创建的<tt>file:</tt> URI在不同操作系统的虚拟机中转换为
     * 抽象路径名时，这种关系通常不成立。
     *
     * @param  uri
     *         一个绝对的、分层的URI，其方案等于<tt>"file"</tt>，
     *         具有非空路径组件，以及未定义的权限、查询和片段组件
     *
     * @throws  NullPointerException
     *          如果<tt>uri</tt>为<tt>null</tt>
     *
     * @throws  IllegalArgumentException
     *          如果参数的前提条件不满足
     *
     * @see #toURI()
     * @see java.net.URI
     * @since 1.4
     */
    public File(URI uri) {

        // 检查我们的许多前提条件
        if (!uri.isAbsolute())
            throw new IllegalArgumentException("URI is not absolute");
        if (uri.isOpaque())
            throw new IllegalArgumentException("URI is not hierarchical");
        String scheme = uri.getScheme();
        if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
            throw new IllegalArgumentException("URI scheme is not \"file\"");
        if (uri.getAuthority() != null)
            throw new IllegalArgumentException("URI has an authority component");
        if (uri.getFragment() != null)
            throw new IllegalArgumentException("URI has a fragment component");
        if (uri.getQuery() != null)
            throw new IllegalArgumentException("URI has a query component");
        String p = uri.getPath();
        if (p.equals(""))
            throw new IllegalArgumentException("URI path component is empty");

        // 好的，现在初始化
        p = fs.fromURIPath(p);
        if (File.separatorChar != '/')
            p = p.replace('/', File.separatorChar);
        this.path = fs.normalize(p);
        this.prefixLength = fs.prefixLength(this.path);
    }


    /* -- 路径组件访问器 -- */

    /**
     * 返回此抽象路径名表示的文件或目录的名称。
     * 这只是路径名名称序列中的最后一个名称。
     * 如果路径名的名称序列为空，则返回空字符串。
     *
     * @return  此抽象路径名表示的文件或目录的名称，
     *          如果此路径名的名称序列为空则返回空字符串
     */
    public String getName() {
        int index = path.lastIndexOf(separatorChar);
        if (index < prefixLength) return path.substring(prefixLength);
        return path.substring(index + 1);
    }

    /**
     * 返回此抽象路径名父级的路径名字符串，如果此路径名
     * 不指定父目录，则返回<code>null</code>。
     *
     * <p> 抽象路径名的<em>父级</em>由路径名的前缀（如果有）
     * 和路径名名称序列中除最后一个之外的每个名称组成。
     * 如果名称序列为空，则路径名不指定父目录。
     *
     * @return  此抽象路径名指定的父目录的路径名字符串，
     *          如果此路径名不指定父级则返回<code>null</code>
     */
    public String getParent() {
        int index = path.lastIndexOf(separatorChar);
        if (index < prefixLength) {
            if ((prefixLength > 0) && (path.length() > prefixLength))
                return path.substring(0, prefixLength);
            return null;
        }
        return path.substring(0, index);
    }

    /**
     * 返回此抽象路径名父级的抽象路径名，如果此路径名
     * 不指定父目录，则返回<code>null</code>。
     *
     * <p> 抽象路径名的<em>父级</em>由路径名的前缀（如果有）
     * 和路径名名称序列中除最后一个之外的每个名称组成。
     * 如果名称序列为空，则路径名不指定父目录。
     *
     * @return  此抽象路径名指定的父目录的抽象路径名，
     *          如果此路径名不指定父级则返回<code>null</code>
     *
     * @since 1.2
     */
    public File getParentFile() {
        String p = this.getParent();
        if (p == null) return null;
        if (getClass() != File.class) {
            p = fs.normalize(p);
        }
        return new File(p, this.prefixLength);
    }

    /**
     * 将此抽象路径名转换为路径名字符串。结果字符串使用
     * {@link #separator 默认名称分隔符字符}来分隔名称序列中的名称。
     *
     * @return  此抽象路径名的字符串形式
     */
    public String getPath() {
        return path;
    }


    /* -- 路径操作 -- */

    /**
     * 测试此抽象路径名是否为绝对路径名。绝对路径名的定义是系统相关的。
     * 在UNIX系统上，如果路径名的前缀是<code>"/"</code>，则路径名是绝对的。
     * 在Microsoft Windows系统上，如果路径名的前缀是驱动器说明符后跟
     * <code>"\\"</code>，或者其前缀是<code>"\\\\"</code>，则路径名是绝对的。
     *
     * @return  如果此抽象路径名是绝对的则返回<code>true</code>，
     *          否则返回<code>false</code>
     */
    public boolean isAbsolute() {
        return fs.isAbsolute(this);
    }

    /**
     * 返回此抽象路径名的绝对路径名字符串。
     *
     * <p> 如果此抽象路径名已经是绝对的，则简单地返回路径名字符串，
     * 就像通过<code>{@link #getPath}</code>方法一样。
     * 如果此抽象路径名是空的抽象路径名，则返回当前用户目录的路径名字符串，
     * 该目录由系统属性<code>user.dir</code>命名。
     * 否则，此路径名以系统相关的方式解析。在UNIX系统上，
     * 相对路径名通过根据当前用户目录解析而变为绝对路径名。
     * 在Microsoft Windows系统上，相对路径名通过根据路径名命名的
     * 驱动器的当前目录（如果有）解析而变为绝对路径名；
     * 如果没有，则根据当前用户目录解析。
     *
     * @return  表示与此抽象路径名相同的文件或目录的绝对路径名字符串
     *
     * @throws  SecurityException
     *          如果无法访问所需的系统属性值。
     *
     * @see     java.io.File#isAbsolute()
     */
    public String getAbsolutePath() {
        return fs.resolve(this);
    }

    /**
     * 返回此抽象路径名的绝对形式。等价于
     * <code>new&nbsp;File(this.{@link #getAbsolutePath})</code>。
     *
     * @return  表示与此抽象路径名相同的文件或目录的绝对抽象路径名
     *
     * @throws  SecurityException
     *          如果无法访问所需的系统属性值。
     *
     * @since 1.2
     */
    public File getAbsoluteFile() {
        String absPath = getAbsolutePath();
        if (getClass() != File.class) {
            absPath = fs.normalize(absPath);
        }
        return new File(absPath, fs.prefixLength(absPath));
    }

    /**
     * 返回此抽象路径名的规范路径名字符串。
     *
     * <p> 规范路径名既是绝对的又是唯一的。规范形式的精确定义是系统相关的。
     * 此方法首先在必要时将此路径名转换为绝对形式，就像调用{@link #getAbsolutePath}方法一样，
     * 然后以系统相关的方式将其映射到其唯一形式。这通常涉及从路径名中删除冗余名称
     * （如<tt>"."</tt>和<tt>".."</tt>），解析符号链接（在UNIX平台上），
     * 以及将驱动器字母转换为标准大小写（在Microsoft Windows平台上）。
     *
     * <p> 表示现有文件或目录的每个路径名都有唯一的规范形式。
     * 表示不存在文件或目录的每个路径名也有唯一的规范形式。
     * 不存在文件或目录的路径名的规范形式可能与创建文件或目录后
     * 相同路径名的规范形式不同。类似地，现有文件或目录的路径名的规范形式
     * 可能与删除文件或目录后相同路径名的规范形式不同。
     *
     * @return  表示与此抽象路径名相同的文件或目录的规范路径名字符串
     *
     * @throws  IOException
     *          如果发生I/O错误，这是可能的，因为构造规范路径名可能需要文件系统查询
     *
     * @throws  SecurityException
     *          如果无法访问所需的系统属性值，或者如果存在安全管理器且其
     *          <code>{@link java.lang.SecurityManager#checkRead}</code>方法
     *          拒绝对文件的读取访问
     *
     * @since   JDK1.1
     * @see     Path#toRealPath
     */
    public String getCanonicalPath() throws IOException {
        if (isInvalid()) {
            throw new IOException("Invalid file path");
        }
        return fs.canonicalize(fs.resolve(this));
    }

    /**
     * 返回此抽象路径名的规范形式。等价于
     * <code>new&nbsp;File(this.{@link #getCanonicalPath})</code>。
     *
     * @return  表示与此抽象路径名相同的文件或目录的规范路径名字符串
     *
     * @throws  IOException
     *          如果发生I/O错误，这是可能的，因为构造规范路径名可能需要文件系统查询
     *
     * @throws  SecurityException
     *          如果无法访问所需的系统属性值，或者如果存在安全管理器且其
     *          <code>{@link java.lang.SecurityManager#checkRead}</code>方法
     *          拒绝对文件的读取访问
     *
     * @since 1.2
     * @see     Path#toRealPath
     */
    public File getCanonicalFile() throws IOException {
        String canonPath = getCanonicalPath();
        if (getClass() != File.class) {
            canonPath = fs.normalize(canonPath);
        }
        return new File(canonPath, fs.prefixLength(canonPath));
    }

    private static String slashify(String path, boolean isDirectory) {
        String p = path;
        if (File.separatorChar != '/')
            p = p.replace(File.separatorChar, '/');
        if (!p.startsWith("/"))
            p = "/" + p;
        if (!p.endsWith("/") && isDirectory)
            p = p + "/";
        return p;
    }

    /**
     * 将此抽象路径名转换为<code>file:</code> URL。
     * URL的确切形式是系统相关的。如果可以确定此抽象路径名
     * 表示的文件是目录，则结果URL将以斜杠结尾。
     *
     * @return  表示等价文件URL的URL对象
     *
     * @throws  MalformedURLException
     *          如果路径无法解析为URL
     *
     * @see     #toURI()
     * @see     java.net.URI
     * @see     java.net.URI#toURL()
     * @see     java.net.URL
     * @since   1.2
     *
     * @deprecated 此方法不会自动转义URL中非法的字符。建议新代码通过首先
     * 将抽象路径名转换为URI（通过{@link #toURI() toURI}方法），然后通过
     * {@link java.net.URI#toURL() URI.toURL}方法将URI转换为URL的方式
     * 将抽象路径名转换为URL。
     */
    @Deprecated
    public URL toURL() throws MalformedURLException {
        if (isInvalid()) {
            throw new MalformedURLException("Invalid file path");
        }
        return new URL("file", "", slashify(getAbsolutePath(), isDirectory()));
    }

    /**
     * 构造表示此抽象路径名的<tt>file:</tt> URI。
     *
     * <p> URI的确切形式是系统相关的。如果可以确定此抽象路径名表示的文件是目录，
     * 则生成的URI将以斜杠结尾。
     *
     * <p> 对于给定的抽象路径名<i>f</i>，保证
     *
     * <blockquote><tt>
     * new {@link #File(java.net.URI) File}(</tt><i>&nbsp;f</i><tt>.toURI()).equals(</tt><i>&nbsp;f</i><tt>.{@link #getAbsoluteFile() getAbsoluteFile}())
     * </tt></blockquote>
     *
     * 只要原始抽象路径名、URI和新抽象路径名都在同一个Java虚拟机的
     * （可能不同的调用中）创建。但是，由于抽象路径名的系统相关性质，
     * 当在一个操作系统的虚拟机中创建的<tt>file:</tt> URI在不同操作系统的
     * 虚拟机中转换为抽象路径名时，这种关系通常不成立。
     *
     * <p> 请注意，当此抽象路径名表示UNC路径名时，UNC的所有组件
     * （包括服务器名称组件）都编码在{@code URI}路径中。权限组件未定义，
     * 意味着它表示为{@code null}。{@link Path}类定义了{@link Path#toUri toUri}
     * 方法来在生成的{@code URI}的权限组件中编码服务器名称。
     * {@link #toPath toPath}方法可用于获取表示此抽象路径名的{@code Path}。
     *
     * @return  一个绝对的、分层的URI，其方案等于<tt>"file"</tt>，
     *          路径表示此抽象路径名，以及未定义的权限、查询和片段组件
     * @throws SecurityException 如果无法访问所需的系统属性值。
     *
     * @see #File(java.net.URI)
     * @see java.net.URI
     * @see java.net.URI#toURL()
     * @since 1.4
     */
    public URI toURI() {
        try {
            File f = getAbsoluteFile();
            String sp = slashify(f.getPath(), f.isDirectory());
            if (sp.startsWith("//"))
                sp = "//" + sp;
            return new URI("file", null, sp, null);
        } catch (URISyntaxException x) {
            throw new Error(x);         // Can't happen
        }
    }


    /* -- Attribute accessors -- */

    /**
     * 测试应用程序是否可以读取此抽象路径名表示的文件。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它读取标记为不可读的文件。
     * 因此，即使文件没有读取权限，此方法也可能返回{@code true}。
     *
     * @return  当且仅当此抽象路径名指定的文件存在<em>且</em>应用程序可以读取时
     *          返回<code>true</code>；否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法拒绝对文件的读取访问
     */
    public boolean canRead() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.checkAccess(this, FileSystem.ACCESS_READ);
    }

    /**
     * 测试应用程序是否可以修改此抽象路径名表示的文件。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它修改标记为只读的文件。
     * 因此，即使文件标记为只读，此方法也可能返回{@code true}。
     *
     * @return  当且仅当文件系统实际包含此抽象路径名表示的文件<em>且</em>
     *          应用程序被允许写入该文件时返回<code>true</code>；
     *          否则返回<code>false</code>。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对文件的写入访问
     */
    public boolean canWrite() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.checkAccess(this, FileSystem.ACCESS_WRITE);
    }

    /**
     * 测试此抽象路径名表示的文件或目录是否存在。
     *
     * @return  当且仅当此抽象路径名表示的文件或目录存在时返回<code>true</code>；
     *          否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法拒绝对文件或目录的读取访问
     */
    public boolean exists() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return false;
        }
        return ((fs.getBooleanAttributes(this) & FileSystem.BA_EXISTS) != 0);
    }

    /**
     * 测试此抽象路径名表示的文件是否为目录。
     *
     * <p> 当需要区分I/O异常与文件不是目录的情况，或者需要同时获取
     * 同一文件的多个属性时，可以使用{@link
     * java.nio.file.Files#readAttributes(Path,Class,LinkOption[])
     * Files.readAttributes}方法。
     *
     * @return 当且仅当此抽象路径名表示的文件存在<em>且</em>是目录时
     *          返回<code>true</code>；否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法拒绝对文件的读取访问
     */
    public boolean isDirectory() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return false;
        }
        return ((fs.getBooleanAttributes(this) & FileSystem.BA_DIRECTORY)
                != 0);
    }

    /**
     * 测试此抽象路径名表示的文件是否为普通文件。如果文件不是目录，
     * 并且满足其他系统相关条件，则该文件是<em>普通</em>文件。
     * Java应用程序创建的任何非目录文件都保证是普通文件。
     *
     * <p> 当需要区分I/O异常与文件不是普通文件的情况，或者需要同时获取
     * 同一文件的多个属性时，可以使用{@link
     * java.nio.file.Files#readAttributes(Path,Class,LinkOption[])
     * Files.readAttributes}方法。
     *
     * @return  当且仅当此抽象路径名表示的文件存在<em>且</em>是普通文件时
     *          返回<code>true</code>；否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法拒绝对文件的读取访问
     */
    public boolean isFile() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return false;
        }
        return ((fs.getBooleanAttributes(this) & FileSystem.BA_REGULAR) != 0);
    }

    /**
     * Tests whether the file named by this abstract pathname is a hidden
     * file.  The exact definition of <em>hidden</em> is system-dependent.  On
     * UNIX systems, a file is considered to be hidden if its name begins with
     * a period character (<code>'.'</code>).  On Microsoft Windows systems, a file is
     * considered to be hidden if it has been marked as such in the filesystem.
     *
     * @return  <code>true</code> if and only if the file denoted by this
     *          abstract pathname is hidden according to the conventions of the
     *          底层平台
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法拒绝对文件的读取访问
     *
     * @since 1.2
     */
    public boolean isHidden() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return false;
        }
        return ((fs.getBooleanAttributes(this) & FileSystem.BA_HIDDEN) != 0);
    }

    /**
     * 返回此抽象路径名表示的文件最后修改的时间。
     *
     * <p> 当需要区分I/O异常与返回{@code 0L}的情况，或者需要同时获取
     * 同一文件的多个属性，或者需要最后访问时间或创建时间时，
     * 可以使用{@link java.nio.file.Files#readAttributes(Path,Class,LinkOption[])
     * Files.readAttributes}方法。
     *
     * @return  表示文件最后修改时间的<code>long</code>值，以自纪元
     *          （1970年1月1日00:00:00 GMT）以来的毫秒数计算，
     *          如果文件不存在或发生I/O错误则返回<code>0L</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法拒绝对文件的读取访问
     */
    public long lastModified() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return 0L;
        }
        return fs.getLastModifiedTime(this);
    }

    /**
     * 返回此抽象路径名表示的文件的长度。如果此路径名表示目录，
     * 则返回值未指定。
     *
     * <p> 当需要区分I/O异常与返回{@code 0L}的情况，或者需要同时获取
     * 同一文件的多个属性时，可以使用{@link
     * java.nio.file.Files#readAttributes(Path,Class,LinkOption[])
     * Files.readAttributes}方法。
     *
     * @return  此抽象路径名表示的文件的长度（以字节为单位），
     *          如果文件不存在则返回<code>0L</code>。某些操作系统可能
     *          对表示系统相关实体（如设备或管道）的路径名返回<code>0L</code>。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法拒绝对文件的读取访问
     */
    public long length() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return 0L;
        }
        return fs.getLength(this);
    }


    /* -- File operations -- */

    /**
     * 当且仅当具有此名称的文件尚不存在时，原子地创建由此抽象路径名命名的
     * 新的空文件。检查文件是否存在以及在文件不存在时创建文件是单个操作，
     * 相对于可能影响该文件的所有其他文件系统活动是原子的。
     * <P>
     * 注意：此方法<i>不应</i>用于文件锁定，因为生成的协议无法可靠地工作。
     * 应该使用{@link java.nio.channels.FileLock FileLock}工具。
     *
     * @return  如果指定文件不存在且成功创建则返回<code>true</code>；
     *          如果指定文件已存在则返回<code>false</code>
     *
     * @throws  IOException
     *          如果发生I/O错误
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对文件的写入访问
     *
     * @since 1.2
     */
    public boolean createNewFile() throws IOException {
        SecurityManager security = System.getSecurityManager();
        if (security != null) security.checkWrite(path);
        if (isInvalid()) {
            throw new IOException("Invalid file path");
        }
        return fs.createFileExclusively(path);
    }

    /**
     * 删除此抽象路径名表示的文件或目录。如果此路径名表示目录，
     * 则该目录必须为空才能被删除。
     *
     * <p> 请注意，{@link java.nio.file.Files}类定义了{@link
     * java.nio.file.Files#delete(Path) delete}方法，当文件无法删除时
     * 抛出{@link IOException}。这对于错误报告和诊断文件无法删除的原因很有用。
     *
     * @return  当且仅当文件或目录成功删除时返回<code>true</code>；
     *          否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkDelete}</code>方法拒绝
     *          对文件的删除访问
     */
    public boolean delete() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkDelete(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.delete(this);
    }

    /**
     * 请求在虚拟机终止时删除此抽象路径名表示的文件或目录。
     * 文件（或目录）按照注册的相反顺序删除。调用此方法删除已注册
     * 删除的文件或目录没有效果。只有在Java语言规范定义的虚拟机
     * 正常终止时才会尝试删除。
     *
     * <p> 一旦请求删除，就不可能取消请求。因此应谨慎使用此方法。
     *
     * <P>
     * 注意：此方法<i>不应</i>用于文件锁定，因为生成的协议无法可靠地工作。
     * 应该使用{@link java.nio.channels.FileLock FileLock}工具。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkDelete}</code>方法拒绝
     *          对文件的删除访问
     *
     * @see #delete
     *
     * @since 1.2
     */
    public void deleteOnExit() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkDelete(path);
        }
        if (isInvalid()) {
            return;
        }
        DeleteOnExitHook.add(path);
    }

    /**
     * Returns an array of strings naming the files and directories in the
     * directory denoted by this abstract pathname.  The strings are
     * ensured to represent normalized paths.
     *
     * @return  An array of strings naming the files and directories in the
     *          directory denoted by this abstract pathname.  The array will be
     *          empty if the directory is empty.  Returns {@code null} if
     *          this abstract pathname does not denote a directory, or if an
     *          I/O error occurs.
     *
     * @throws  SecurityException
     *          If a security manager exists and its {@link
     *          SecurityManager#checkRead(String)} method denies read access to
     *          the directory
     */
    private final String[] normalizedList() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(path);
        }
        if (isInvalid()) {
            return null;
        }
        String[] s = fs.list(this);
        if (s != null && getClass() != File.class) {
            String[] normalized = new String[s.length];
            for (int i = 0; i < s.length; i++) {
                normalized[i] = fs.normalize(s[i]);
            }
            s = normalized;
        }
        return s;
    }

    /**
     * 返回一个字符串数组，命名此抽象路径名表示的目录中的文件和目录。
     *
     * <p> 如果此抽象路径名不表示目录，则此方法返回{@code null}。
     * 否则返回字符串数组，目录中的每个文件或目录对应一个字符串。
     * 表示目录本身和目录父目录的名称不包含在结果中。每个字符串是
     * 文件名而不是完整路径。
     *
     * <p> 不保证结果数组中的名称字符串以任何特定顺序出现；
     * 特别是，不保证按字母顺序出现。
     *
     * <p> 请注意，{@link java.nio.file.Files}类定义了{@link
     * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream}方法
     * 来打开目录并迭代目录中文件的名称。在处理非常大的目录时，这可能使用
     * 更少的资源，在处理远程目录时可能响应更快。
     *
     * @return  命名此抽象路径名表示的目录中文件和目录的字符串数组。
     *          如果目录为空，则数组将为空。如果此抽象路径名不表示目录
     *          或发生I/O错误，则返回{@code null}。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其{@link
     *          SecurityManager#checkRead(String)}方法拒绝对目录的读取访问
     */
    public String[] list() {
        return normalizedList();
    }

    /**
     * 返回一个字符串数组，命名此抽象路径名表示的目录中满足指定过滤器的
     * 文件和目录。此方法的行为与{@link #list()}方法相同，除了返回数组中的
     * 字符串必须满足过滤器。如果给定的{@code filter}为{@code null}，
     * 则接受所有名称。否则，当且仅当在此抽象路径名和它表示的目录中
     * 文件或目录的名称上调用过滤器的{@link FilenameFilter#accept 
     * FilenameFilter.accept(File,&nbsp;String)}方法时结果为{@code true}，
     * 名称才满足过滤器。
     *
     * @param  filter
     *         文件名过滤器
     *
     * @return  命名此抽象路径名表示的目录中被给定{@code filter}接受的
     *          文件和目录的字符串数组。如果目录为空或没有名称被过滤器接受，
     *          则数组将为空。如果此抽象路径名不表示目录或发生I/O错误，
     *          则返回{@code null}。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其{@link
     *          SecurityManager#checkRead(String)}方法拒绝对目录的读取访问
     *
     * @see java.nio.file.Files#newDirectoryStream(Path,String)
     */
    public String[] list(FilenameFilter filter) {
        String names[] = normalizedList();
        if ((names == null) || (filter == null)) {
            return names;
        }
        List<String> v = new ArrayList<>();
        for (int i = 0 ; i < names.length ; i++) {
            if (filter.accept(this, names[i])) {
                v.add(names[i]);
            }
        }
        return v.toArray(new String[v.size()]);
    }

    /**
     * 返回一个抽象路径名数组，表示此抽象路径名表示的目录中的文件。
     *
     * <p> 如果此抽象路径名不表示目录，则此方法返回{@code null}。
     * 否则返回{@code File}对象数组，目录中的每个文件或目录对应一个对象。
     * 表示目录本身和目录父目录的路径名不包含在结果中。每个结果抽象路径名
     * 都是使用{@link #File(File, String) File(File,&nbsp;String)}构造函数
     * 从此抽象路径名构造的。因此，如果此路径名是绝对的，则每个结果路径名
     * 都是绝对的；如果此路径名是相对的，则每个结果路径名都相对于同一目录。
     *
     * <p> 不保证结果数组中的名称字符串以任何特定顺序出现；
     * 特别是，不保证按字母顺序出现。
     *
     * <p> 请注意，{@link java.nio.file.Files}类定义了{@link
     * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream}方法
     * 来打开目录并迭代目录中文件的名称。在处理非常大的目录时，
     * 这可能使用更少的资源。
     *
     * @return  表示此抽象路径名表示的目录中文件和目录的抽象路径名数组。
     *          如果目录为空，则数组将为空。如果此抽象路径名不表示目录
     *          或发生I/O错误，则返回{@code null}。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其{@link
     *          SecurityManager#checkRead(String)}方法拒绝对目录的读取访问
     *
     * @since  1.2
     */
    public File[] listFiles() {
        String[] ss = normalizedList();
        if (ss == null) return null;
        int n = ss.length;
        File[] fs = new File[n];
        for (int i = 0; i < n; i++) {
            fs[i] = new File(ss[i], this);
        }
        return fs;
    }

    /**
     * 返回一个抽象路径名数组，表示此抽象路径名表示的目录中满足指定过滤器的
     * 文件和目录。此方法的行为与{@link #listFiles()}方法相同，除了返回数组中的
     * 路径名必须满足过滤器。如果给定的{@code filter}为{@code null}，
     * 则接受所有路径名。否则，当且仅当在此抽象路径名和它表示的目录中
     * 文件或目录的名称上调用过滤器的{@link FilenameFilter#accept
     * FilenameFilter.accept(File,&nbsp;String)}方法时结果为{@code true}，
     * 路径名才满足过滤器。
     *
     * @param  filter
     *         文件名过滤器
     *
     * @return  表示此抽象路径名表示的目录中文件和目录的抽象路径名数组。
     *          如果目录为空，则数组将为空。如果此抽象路径名不表示目录
     *          或发生I/O错误，则返回{@code null}。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其{@link
     *          SecurityManager#checkRead(String)}方法拒绝对目录的读取访问
     *
     * @since  1.2
     * @see java.nio.file.Files#newDirectoryStream(Path,String)
     */
    public File[] listFiles(FilenameFilter filter) {
        String ss[] = normalizedList();
        if (ss == null) return null;
        ArrayList<File> files = new ArrayList<>();
        for (String s : ss)
            if ((filter == null) || filter.accept(this, s))
                files.add(new File(s, this));
        return files.toArray(new File[files.size()]);
    }

    /**
     * Returns an array of abstract pathnames denoting the files and
     * directories in the directory denoted by this abstract pathname that
     * satisfy the specified filter.  The behavior of this method is the same
     * as that of the {@link #listFiles()} method, except that the pathnames in
     * the returned array must satisfy the filter.  If the given {@code filter}
     * is {@code null} then all pathnames are accepted.  Otherwise, a pathname
     * satisfies the filter if and only if the value {@code true} results when
     * the {@link FileFilter#accept FileFilter.accept(File)} method of the
     * filter is invoked on the pathname.
     *
     * @param  filter
     *         A file filter
     *
     * @return  An array of abstract pathnames denoting the files and
     *          directories in the directory denoted by this abstract pathname.
     *          The array will be empty if the directory is empty.  Returns
     *          {@code null} if this abstract pathname does not denote a
     *          directory, or if an I/O error occurs.
     *
     * @throws  SecurityException
     *          If a security manager exists and its {@link
     *          SecurityManager#checkRead(String)} method denies read access to
     *          the directory
     *
     * @since  1.2
     * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter)
     */
    public File[] listFiles(FileFilter filter) {
        String ss[] = normalizedList();
        if (ss == null) return null;
        ArrayList<File> files = new ArrayList<>();
        for (String s : ss) {
            File f = new File(s, this);
            if ((filter == null) || filter.accept(f))
                files.add(f);
        }
        return files.toArray(new File[files.size()]);
    }

    /**
     * Creates the directory named by this abstract pathname.
     *
     * @return  <code>true</code> if and only if the directory was
     *          created; <code>false</code> otherwise
     *
     * @throws  SecurityException
     *          If a security manager exists and its <code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          method does not permit the named directory to be created
     */
    public boolean mkdir() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.createDirectory(this);
    }

    /**
     * 创建此抽象路径名命名的目录，包括任何必要但不存在的父目录。
     * 请注意，如果此操作失败，它可能已成功创建了一些必要的父目录。
     *
     * @return  当且仅当目录与所有必要的父目录一起创建时返回<code>true</code>；
     *          否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkRead(java.lang.String)}</code>
     *          方法不允许验证指定目录和所有必要父目录的存在；或者如果
     *          <code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法不允许创建指定目录和所有必要的父目录
     */
    public boolean mkdirs() {
        if (exists()) {
            return false;
        }
        if (mkdir()) {
            return true;
        }
        File canonFile = null;
        try {
            canonFile = getCanonicalFile();
        } catch (IOException e) {
            return false;
        }

        File parent = canonFile.getParentFile();
        return (parent != null && (parent.mkdirs() || parent.exists()) &&
                canonFile.mkdir());
    }

    /**
     * 重命名此抽象路径名表示的文件。
     *
     * <p> 此方法行为的许多方面本质上是平台相关的：重命名操作可能无法
     * 将文件从一个文件系统移动到另一个文件系统，它可能不是原子的，
     * 如果具有目标抽象路径名的文件已存在，它可能不会成功。应始终检查
     * 返回值以确保重命名操作成功。
     *
     * <p> 请注意，{@link java.nio.file.Files}类定义了{@link
     * java.nio.file.Files#move move}方法，以平台无关的方式移动或重命名文件。
     *
     * @param  dest  指定文件的新抽象路径名
     *
     * @return  当且仅当重命名成功时返回<code>true</code>；
     *          否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对旧路径名或新路径名的写入访问
     *
     * @throws  NullPointerException
     *          如果参数<code>dest</code>为<code>null</code>
     */
    public boolean renameTo(File dest) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
            security.checkWrite(dest.path);
        }
        if (dest == null) {
            throw new NullPointerException();
        }
        if (this.isInvalid() || dest.isInvalid()) {
            return false;
        }
        return fs.rename(this, dest);
    }

    /**
     * 设置此抽象路径名命名的文件或目录的最后修改时间。
     *
     * <p> 所有平台都支持精确到秒的文件修改时间，但有些平台提供更高的精度。
     * 参数将被截断以适应支持的精度。如果操作成功且没有对文件进行干预操作，
     * 则下次调用<code>{@link #lastModified}</code>方法将返回传递给此方法的
     * （可能被截断的）<code>time</code>参数。
     *
     * @param  time  新的最后修改时间，以自纪元（1970年1月1日00:00:00 GMT）
     *               以来的毫秒数计算
     *
     * @return 当且仅当操作成功时返回<code>true</code>；
     *          否则返回<code>false</code>
     *
     * @throws  IllegalArgumentException  如果参数为负数
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对指定文件的写入访问
     *
     * @since 1.2
     */
    public boolean setLastModified(long time) {
        if (time < 0) throw new IllegalArgumentException("Negative time");
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.setLastModifiedTime(this, time);
    }

    /**
     * 标记此抽象路径名命名的文件或目录，使其只允许读取操作。
     * 调用此方法后，文件或目录将不会更改，直到它被删除或标记为允许写入访问。
     * 在某些平台上，可能可以使用特殊权限启动Java虚拟机，允许它修改
     * 标记为只读的文件。只读文件或目录是否可以被删除取决于底层系统。
     *
     * @return 当且仅当操作成功时返回<code>true</code>；
     *          否则返回<code>false</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对指定文件的写入访问
     *
     * @since 1.2
     */
    public boolean setReadOnly() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.setReadOnly(this);
    }

    /**
     * 为此抽象路径名设置所有者或所有人的写入权限。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它修改不允许写入操作的文件。
     *
     * <p> {@link java.nio.file.Files}类定义了操作文件属性（包括文件权限）的方法。
     * 当需要更精细的文件权限操作时，可以使用这些方法。
     *
     * @param   writable
     *          如果为<code>true</code>，设置访问权限以允许写入操作；
     *          如果为<code>false</code>则不允许写入操作
     *
     * @param   ownerOnly
     *          如果为<code>true</code>，写入权限仅适用于所有者的写入权限；
     *          否则，它适用于所有人。如果底层文件系统无法区分所有者的写入权限
     *          与其他人的写入权限，则无论此值如何，权限都将适用于所有人。
     *
     * @return  当且仅当操作成功时返回<code>true</code>。如果用户没有权限
     *          更改此抽象路径名的访问权限，则操作将失败。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对指定文件的写入访问
     *
     * @since 1.6
     */
    public boolean setWritable(boolean writable, boolean ownerOnly) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.setPermission(this, FileSystem.ACCESS_WRITE, writable, ownerOnly);
    }

    /**
     * 为此抽象路径名设置所有者写入权限的便捷方法。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它修改不允许写入操作的文件。
     *
     * <p> 调用形式为<tt>file.setWritable(arg)</tt>的此方法的行为
     * 与以下调用完全相同：
     *
     * <pre>
     *     file.setWritable(arg, true) </pre>
     *
     * @param   writable
     *          如果为<code>true</code>，设置访问权限以允许写入操作；
     *          如果为<code>false</code>则不允许写入操作
     *
     * @return  当且仅当操作成功时返回<code>true</code>。如果用户没有权限
     *          更改此抽象路径名的访问权限，则操作将失败。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对文件的写入访问
     *
     * @since 1.6
     */
    public boolean setWritable(boolean writable) {
        return setWritable(writable, true);
    }

    /**
     * 为此抽象路径名设置所有者或所有人的读取权限。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它读取标记为不可读的文件。
     *
     * <p> {@link java.nio.file.Files}类定义了操作文件属性（包括文件权限）的方法。
     * 当需要更精细的文件权限操作时，可以使用这些方法。
     *
     * @param   readable
     *          如果为<code>true</code>，设置访问权限以允许读取操作；
     *          如果为<code>false</code>则不允许读取操作
     *
     * @param   ownerOnly
     *          如果为<code>true</code>，读取权限仅适用于所有者的读取权限；
     *          否则，它适用于所有人。如果底层文件系统无法区分所有者的读取权限
     *          与其他人的读取权限，则无论此值如何，权限都将适用于所有人。
     *
     * @return  当且仅当操作成功时返回<code>true</code>。如果用户没有权限
     *          更改此抽象路径名的访问权限，则操作将失败。如果<code>readable</code>
     *          为<code>false</code>且底层文件系统未实现读取权限，则操作将失败。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对文件的写入访问
     *
     * @since 1.6
     */
    public boolean setReadable(boolean readable, boolean ownerOnly) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.setPermission(this, FileSystem.ACCESS_READ, readable, ownerOnly);
    }

    /**
     * 为此抽象路径名设置所有者读取权限的便捷方法。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它读取标记为不可读的文件。
     *
     * <p>调用形式为<tt>file.setReadable(arg)</tt>的此方法的行为
     * 与以下调用完全相同：
     *
     * <pre>
     *     file.setReadable(arg, true) </pre>
     *
     * @param  readable
     *          如果为<code>true</code>，设置访问权限以允许读取操作；
     *          如果为<code>false</code>则不允许读取操作
     *
     * @return  当且仅当操作成功时返回<code>true</code>。如果用户没有权限
     *          更改此抽象路径名的访问权限，则操作将失败。如果<code>readable</code>
     *          为<code>false</code>且底层文件系统未实现读取权限，则操作将失败。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对文件的写入访问
     *
     * @since 1.6
     */
    public boolean setReadable(boolean readable) {
        return setReadable(readable, true);
    }

    /**
     * 为此抽象路径名设置所有者或所有人的执行权限。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它执行未标记为可执行的文件。
     *
     * <p> {@link java.nio.file.Files}类定义了操作文件属性（包括文件权限）的方法。
     * 当需要更精细的文件权限操作时，可以使用这些方法。
     *
     * @param   executable
     *          如果为<code>true</code>，设置访问权限以允许执行操作；
     *          如果为<code>false</code>则不允许执行操作
     *
     * @param   ownerOnly
     *          如果为<code>true</code>，执行权限仅适用于所有者的执行权限；
     *          否则，它适用于所有人。如果底层文件系统无法区分所有者的
     *          执行权限与其他人的执行权限，则无论此值如何，权限都将适用于所有人。
     *
     * @return  当且仅当操作成功时返回<code>true</code>。如果用户没有权限
     *          更改此抽象路径名的访问权限，则操作将失败。如果<code>executable</code>
     *          为<code>false</code>且底层文件系统未实现执行权限，则操作将失败。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对文件的写入访问
     *
     * @since 1.6
     */
    public boolean setExecutable(boolean executable, boolean ownerOnly) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.setPermission(this, FileSystem.ACCESS_EXECUTE, executable, ownerOnly);
    }

    /**
     * 为此抽象路径名设置所有者执行权限的便捷方法。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它执行未标记为可执行的文件。
     *
     * <p>调用形式为<tt>file.setExcutable(arg)</tt>的此方法的行为
     * 与以下调用完全相同：
     *
     * <pre>
     *     file.setExecutable(arg, true) </pre>
     *
     * @param   executable
     *          如果为<code>true</code>，设置访问权限以允许执行操作；
     *          如果为<code>false</code>则不允许执行操作
     *
     * @return   当且仅当操作成功时返回<code>true</code>。如果用户没有权限
     *           更改此抽象路径名的访问权限，则操作将失败。如果<code>executable</code>
     *           为<code>false</code>且底层文件系统未实现执行权限，则操作将失败。
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法拒绝对文件的写入访问
     *
     * @since 1.6
     */
    public boolean setExecutable(boolean executable) {
        return setExecutable(executable, true);
    }

    /**
     * 测试应用程序是否可以执行此抽象路径名表示的文件。在某些平台上，
     * 可能可以使用特殊权限启动Java虚拟机，允许它执行未标记为可执行的文件。
     * 因此，即使文件没有执行权限，此方法也可能返回{@code true}。
     *
     * @return  当且仅当抽象路径名存在<em>且</em>应用程序被允许执行文件时
     *          返回<code>true</code>
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkExec(java.lang.String)}</code>
     *          方法拒绝对文件的执行访问
     *
     * @since 1.6
     */
    public boolean canExecute() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkExec(path);
        }
        if (isInvalid()) {
            return false;
        }
        return fs.checkAccess(this, FileSystem.ACCESS_EXECUTE);
    }


    /* -- Filesystem interface -- */

    /**
     * 列出可用的文件系统根目录。
     *
     * <p> 特定的Java平台可能支持零个或多个分层组织的文件系统。
     * 每个文件系统都有一个{@code root}目录，从该目录可以访问该文件系统中的
     * 所有其他文件。例如，Windows平台为每个活动驱动器都有一个根目录；
     * UNIX平台有一个根目录，即{@code "/"}。可用文件系统根目录的集合
     * 受各种系统级操作的影响，如插入或弹出可移动媒体以及断开或卸载
     * 物理或虚拟磁盘驱动器。
     *
     * <p> 此方法返回一个{@code File}对象数组，表示可用文件系统根目录的
     * 根目录。保证本地机器上物理存在的任何文件的规范路径名都将以
     * 此方法返回的根目录之一开始。
     *
     * <p> 驻留在其他机器上并通过远程文件系统协议（如SMB或NFS）访问的
     * 文件的规范路径名可能以此方法返回的根目录之一开始，也可能不以其开始。
     * 如果远程文件的路径名在语法上与本地文件的路径名无法区分，
     * 则它将以此方法返回的根目录之一开始。因此，例如，表示Windows平台
     * 映射网络驱动器根目录的{@code File}对象将由此方法返回，
     * 而包含UNC路径名的{@code File}对象将不会由此方法返回。
     *
     * <p> 与此类中的大多数方法不同，此方法不抛出安全异常。
     * 如果存在安全管理器且其{@link SecurityManager#checkRead(String)}方法
     * 拒绝对特定根目录的读取访问，则该目录将不会出现在结果中。
     *
     * @return  表示可用文件系统根目录的{@code File}对象数组，
     *          如果无法确定根目录集合则返回{@code null}。
     *          如果没有文件系统根目录，则数组将为空。
     *
     * @since  1.2
     * @see java.nio.file.FileStore
     */
    public static File[] listRoots() {
        return fs.listRoots();
    }


    /* -- Disk usage -- */

    /**
     * 返回此抽象路径名<a href="#partName">命名</a>的分区的大小。
     *
     * @return  分区的大小（以字节为单位），如果此抽象路径名不命名分区
     *          则返回<tt>0L</tt>
     *
     * @throws  SecurityException
     *          如果已安装安全管理器且它拒绝
     *          {@link RuntimePermission}<tt>("getFileSystemAttributes")</tt>
     *          或其{@link SecurityManager#checkRead(String)}方法拒绝
     *          对此抽象路径名命名的文件的读取访问
     *
     * @since  1.6
     */
    public long getTotalSpace() {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
            sm.checkRead(path);
        }
        if (isInvalid()) {
            return 0L;
        }
        return fs.getSpace(this, FileSystem.SPACE_TOTAL);
    }

    /**
     * 返回此抽象路径名<a href="#partName">命名</a>的分区中未分配的字节数。
     *
     * <p> 返回的未分配字节数是一个提示，但不保证可以使用这些字节的
     * 大部分或任何部分。未分配字节数在此调用后立即最可能是准确的。
     * 任何外部I/O操作（包括在此虚拟机外部的系统上进行的操作）都可能
     * 使其变得不准确。此方法不保证对此文件系统的写入操作会成功。
     *
     * @return  分区上未分配的字节数，如果抽象路径名不命名分区则返回<tt>0L</tt>。
     *          此值将小于或等于{@link #getTotalSpace}返回的总文件系统大小。
     *
     * @throws  SecurityException
     *          如果已安装安全管理器且它拒绝
     *          {@link RuntimePermission}<tt>("getFileSystemAttributes")</tt>
     *          或其{@link SecurityManager#checkRead(String)}方法拒绝
     *          对此抽象路径名命名的文件的读取访问
     *
     * @since  1.6
     */
    public long getFreeSpace() {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
            sm.checkRead(path);
        }
        if (isInvalid()) {
            return 0L;
        }
        return fs.getSpace(this, FileSystem.SPACE_FREE);
    }

    /**
     * 返回此抽象路径名<a href="#partName">命名</a>的分区上此虚拟机可用的字节数。
     * 在可能的情况下，此方法检查写入权限和其他操作系统限制，因此通常会提供
     * 比{@link #getFreeSpace}更准确的实际可写入新数据量的估计。
     *
     * <p> 返回的可用字节数是一个提示，但不保证可以使用这些字节的
     * 大部分或任何部分。未分配字节数在此调用后立即最可能是准确的。
     * 任何外部I/O操作（包括在此虚拟机外部的系统上进行的操作）都可能
     * 使其变得不准确。此方法不保证对此文件系统的写入操作会成功。
     *
     * @return  分区上可用的字节数，如果抽象路径名不命名分区则返回<tt>0L</tt>。
     *          在此信息不可用的系统上，此方法将等效于调用{@link #getFreeSpace}。
     *
     * @throws  SecurityException
     *          如果已安装安全管理器且它拒绝
     *          {@link RuntimePermission}<tt>("getFileSystemAttributes")</tt>
     *          或其{@link SecurityManager#checkRead(String)}方法拒绝
     *          对此抽象路径名命名的文件的读取访问
     *
     * @since  1.6
     */
    public long getUsableSpace() {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
            sm.checkRead(path);
        }
        if (isInvalid()) {
            return 0L;
        }
        return fs.getSpace(this, FileSystem.SPACE_USABLE);
    }

    /* -- Temporary files -- */

    private static class TempDirectory {
        private TempDirectory() { }

        // temporary directory location
        private static final File tmpdir = new File(AccessController
            .doPrivileged(new GetPropertyAction("java.io.tmpdir")));
        static File location() {
            return tmpdir;
        }

        // file name generation
        private static final SecureRandom random = new SecureRandom();
        static File generateFile(String prefix, String suffix, File dir)
            throws IOException
        {
            long n = random.nextLong();
            if (n == Long.MIN_VALUE) {
                n = 0;      // corner case
            } else {
                n = Math.abs(n);
            }

            // Use only the file name from the supplied prefix
            prefix = (new File(prefix)).getName();

            String name = prefix + Long.toString(n) + suffix;
            File f = new File(dir, name);
            if (!name.equals(f.getName()) || f.isInvalid()) {
                if (System.getSecurityManager() != null)
                    throw new IOException("Unable to create temporary file");
                else
                    throw new IOException("Unable to create temporary file, " + f);
            }
            return f;
        }
    }

    /**
     * <p> Creates a new empty file in the specified directory, using the
     * given prefix and suffix strings to generate its name.  If this method
     * returns successfully then it is guaranteed that:
     *
     * <ol>
     * <li> The file denoted by the returned abstract pathname did not exist
     *      before this method was invoked, and
     * <li> Neither this method nor any of its variants will return the same
     *      abstract pathname again in the current invocation of the virtual
     *      machine.
     * </ol>
     *
     * This method provides only part of a temporary-file facility.  To arrange
     * for a file created by this method to be deleted automatically, use the
     * <code>{@link #deleteOnExit}</code> method.
     *
     * <p> The <code>prefix</code> argument must be at least three characters
     * long.  It is recommended that the prefix be a short, meaningful string
     * such as <code>"hjb"</code> or <code>"mail"</code>.  The
     * <code>suffix</code> argument may be <code>null</code>, in which case the
     * suffix <code>".tmp"</code> will be used.
     *
     * <p> To create the new file, the prefix and the suffix may first be
     * adjusted to fit the limitations of the underlying platform.  If the
     * prefix is too long then it will be truncated, but its first three
     * characters will always be preserved.  If the suffix is too long then it
     * too will be truncated, but if it begins with a period character
     * (<code>'.'</code>) then the period and the first three characters
     * following it will always be preserved.  Once these adjustments have been
     * made the name of the new file will be generated by concatenating the
     * prefix, five or more internally-generated characters, and the suffix.
     *
     * <p> If the <code>directory</code> argument is <code>null</code> then the
     * system-dependent default temporary-file directory will be used.  The
     * default temporary-file directory is specified by the system property
     * <code>java.io.tmpdir</code>.  On UNIX systems the default value of this
     * property is typically <code>"/tmp"</code> or <code>"/var/tmp"</code>; on
     * Microsoft Windows systems it is typically <code>"C:\\WINNT\\TEMP"</code>.  A different
     * value may be given to this system property when the Java virtual machine
     * is invoked, but programmatic changes to this property are not guaranteed
     * to have any effect upon the temporary directory used by this method.
     *
     * @param  prefix     The prefix string to be used in generating the file's
     *                    name; must be at least three characters long
     *
     * @param  suffix     The suffix string to be used in generating the file's
     *                    name; may be <code>null</code>, in which case the
     *                    suffix <code>".tmp"</code> will be used
     *
     * @param  directory  The directory in which the file is to be created, or
     *                    <code>null</code> if the default temporary-file
     *                    directory is to be used
     *
     * @return  An abstract pathname denoting a newly-created empty file
     *
     * @throws  IllegalArgumentException
     *          If the <code>prefix</code> argument contains fewer than three
     *          characters
     *
     * @throws  IOException  If a file could not be created
     *
     * @throws  SecurityException
     *          If a security manager exists and its <code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          method does not allow a file to be created
     *
     * @since 1.2
     */
    public static File createTempFile(String prefix, String suffix,
                                      File directory)
        throws IOException
    {
        if (prefix.length() < 3)
            throw new IllegalArgumentException("Prefix string too short");
        if (suffix == null)
            suffix = ".tmp";

        File tmpdir = (directory != null) ? directory
                                          : TempDirectory.location();
        SecurityManager sm = System.getSecurityManager();
        File f;
        do {
            f = TempDirectory.generateFile(prefix, suffix, tmpdir);

            if (sm != null) {
                try {
                    sm.checkWrite(f.getPath());
                } catch (SecurityException se) {
                    // don't reveal temporary directory location
                    if (directory == null)
                        throw new SecurityException("Unable to create temporary file");
                    throw se;
                }
            }
        } while ((fs.getBooleanAttributes(f) & FileSystem.BA_EXISTS) != 0);

        if (!fs.createFileExclusively(f.getPath()))
            throw new IOException("Unable to create temporary file");

        return f;
    }

    /**
     * 在默认临时文件目录中创建一个空文件，使用给定的前缀和后缀生成其名称。
     * 调用此方法等效于调用<code>{@link #createTempFile(java.lang.String,
     * java.lang.String, java.io.File)
     * createTempFile(prefix,&nbsp;suffix,&nbsp;null)}</code>。
     *
     * <p> {@link
     * java.nio.file.Files#createTempFile(String,String,java.nio.file.attribute.FileAttribute[])
     * Files.createTempFile}方法提供了在临时文件目录中创建空文件的替代方法。
     * 该方法创建的文件可能比此方法创建的文件具有更严格的访问权限，
     * 因此可能更适合安全敏感的应用程序。
     *
     * @param  prefix     用于生成文件名的前缀字符串；必须至少三个字符长
     *
     * @param  suffix     用于生成文件名的后缀字符串；可以为<code>null</code>，
     *                    在这种情况下将使用后缀<code>".tmp"</code>
     *
     * @return  表示新创建的空文件的抽象路径名
     *
     * @throws  IllegalArgumentException
     *          如果<code>prefix</code>参数包含少于三个字符
     *
     * @throws  IOException  如果无法创建文件
     *
     * @throws  SecurityException
     *          如果存在安全管理器且其<code>{@link
     *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
     *          方法不允许创建文件
     *
     * @since 1.2
     * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[])
     */
    public static File createTempFile(String prefix, String suffix)
        throws IOException
    {
        return createTempFile(prefix, suffix, null);
    }

    /* -- Basic infrastructure -- */

    /**
     * 按字典顺序比较两个抽象路径名。此方法定义的排序取决于底层系统。
     * 在UNIX系统上，字母大小写在比较路径名时是重要的；
     * 在Microsoft Windows系统上则不是。
     *
     * @param   pathname  要与此抽象路径名比较的抽象路径名
     *
     * @return  如果参数等于此抽象路径名则返回零，如果此抽象路径名
     *          在字典顺序上小于参数则返回小于零的值，如果此抽象路径名
     *          在字典顺序上大于参数则返回大于零的值
     *
     * @since   1.2
     */
    public int compareTo(File pathname) {
        return fs.compare(this, pathname);
    }

    /**
     * 测试此抽象路径名与给定对象的相等性。当且仅当参数不为<code>null</code>
     * 且是表示与此抽象路径名相同文件或目录的抽象路径名时，返回<code>true</code>。
     * 两个抽象路径名是否相等取决于底层系统。在UNIX系统上，字母大小写在
     * 比较路径名时是重要的；在Microsoft Windows系统上则不是。
     *
     * @param   obj   要与此抽象路径名比较的对象
     *
     * @return  当且仅当对象相同时返回<code>true</code>；
     *          否则返回<code>false</code>
     */
    public boolean equals(Object obj) {
        if ((obj != null) && (obj instanceof File)) {
            return compareTo((File)obj) == 0;
        }
        return false;
    }

    /**
     * 计算此抽象路径名的哈希码。由于抽象路径名的相等性本质上是系统相关的，
     * 因此它们的哈希码计算也是如此。在UNIX系统上，抽象路径名的哈希码
     * 等于其路径名字符串的哈希码与十进制值<code>1234321</code>的异或。
     * 在Microsoft Windows系统上，哈希码等于其路径名字符串转换为小写后的
     * 哈希码与十进制值<code>1234321</code>的异或。在将路径名字符串转换为
     * 小写时不考虑区域设置。
     *
     * @return  此抽象路径名的哈希码
     */
    public int hashCode() {
        return fs.hashCode(this);
    }

    /**
     * 返回此抽象路径名的路径名字符串。这就是<code>{@link #getPath}</code>
     * 方法返回的字符串。
     *
     * @return  此抽象路径名的字符串形式
     */
    public String toString() {
        return getPath();
    }

    /**
     * 调用WriteObject来保存此文件名。
     * 分隔符字符也被保存，以便在不同主机类型上重构路径时可以替换它。
     * <p>
     * @serialData  默认字段后跟分隔符字符。
     */
    private synchronized void writeObject(java.io.ObjectOutputStream s)
        throws IOException
    {
        s.defaultWriteObject();
        s.writeChar(separatorChar); // Add the separator character
    }

    /**
     * 调用readObject来恢复此文件名。
     * 读取原始分隔符字符。如果它与此系统上的分隔符字符不同，
     * 则用本地分隔符替换旧分隔符。
     */
    private synchronized void readObject(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
        ObjectInputStream.GetField fields = s.readFields();
        String pathField = (String)fields.get("path", null);
        char sep = s.readChar(); // read the previous separator char
        if (sep != separatorChar)
            pathField = pathField.replace(sep, separatorChar);
        String path = fs.normalize(pathField);
        UNSAFE.putObject(this, PATH_OFFSET, path);
        UNSAFE.putIntVolatile(this, PREFIX_LENGTH_OFFSET, fs.prefixLength(path));
    }

    private static final long PATH_OFFSET;
    private static final long PREFIX_LENGTH_OFFSET;
    private static final sun.misc.Unsafe UNSAFE;
    static {
        try {
            sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
            PATH_OFFSET = unsafe.objectFieldOffset(
                    File.class.getDeclaredField("path"));
            PREFIX_LENGTH_OFFSET = unsafe.objectFieldOffset(
                    File.class.getDeclaredField("prefixLength"));
            UNSAFE = unsafe;
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
    }


    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = 301077366599181567L;

    // -- Integration with java.nio.file --

    private volatile transient Path filePath;

    /**
     * 返回从此抽象路径构造的{@link Path java.nio.file.Path}对象。
     * 生成的{@code Path}与{@link java.nio.file.FileSystems#getDefault 默认文件系统}关联。
     *
     * <p> 此方法的第一次调用的工作方式就像调用它等效于计算表达式：
     * <blockquote><pre>
     * {@link java.nio.file.FileSystems#getDefault FileSystems.getDefault}().{@link
     * java.nio.file.FileSystem#getPath getPath}(this.{@link #getPath getPath}());
     * </pre></blockquote>
     * 此方法的后续调用返回相同的{@code Path}。
     *
     * <p> 如果此抽象路径名是空抽象路径名，则此方法返回可用于访问
     * 当前用户目录的{@code Path}。
     *
     * @return  从此抽象路径构造的{@code Path}
     *
     * @throws  java.nio.file.InvalidPathException
     *          如果无法从抽象路径构造{@code Path}对象
     *          （参见{@link java.nio.file.FileSystem#getPath FileSystem.getPath}）
     *
     * @since   1.7
     * @see Path#toFile
     */
    public Path toPath() {
        Path result = filePath;
        if (result == null) {
            synchronized (this) {
                result = filePath;
                if (result == null) {
                    result = FileSystems.getDefault().getPath(path);
                    filePath = result;
                }
            }
        }
        return result;
    }
}
